<?php

class SQL
{
	private static $instance = null;
	private $link;

	private function __construct()
   {
		$conf = config_get('MYSQL');

		$this->link = @mysql_connect($conf['server'], $conf['username'], $conf['password']);
		if ($this->link === FALSE) throw new Exception;
		mysql_select_db($conf['db'], $this->link);
		mysql_query('SET NAMES utf8', $this->link);
	}

	public static function get_writer_instance()
   {
		if (is_null(self::$instance))
      {
			self::$instance = new SQL;
		}

		return self::$instance;
	}

	public static function get_reader_instance()
   {
      return self::get_writer_instance();
   }

	function substitute_params_array($query, $array)
   {
		$conf = config_get('MYSQL');
		$len = mb_strlen($query);
		$ret = '';
      
		for ($i = 0; $i < $len; $i++)
      {
			$char = mb_substr($query, $i, 1);

			if ($char != '?')
         {
				$ret .= $char;
				continue;
			}

			if ($i == $len-1)
         {
				$param = array_shift($array);
            
				if (!is_null($param))
            {
					$param = mysql_real_escape_string($param, $this->link);
					$ret .= "\"$param\"";
				}
            else
            {
					$ret .= 'NULL';
				}
            
				return $ret;
			}

			$nextchar = mb_substr($query, $i+1, 1);

			if ($nextchar == '_') 
         {
				$ret .= $conf['prefix'];
				$i++;
				continue;
			}

			if ($nextchar == 'h')
         {
				$param = array_shift($array);

				$arr = array();

				foreach ($param as $k => $v)
            {
					if (!is_null($v))
               {
						$v = mysql_real_escape_string($v, $this->link);
						$arr[] = "`$k`=\"$v\"";
					}
               else
               {
						$arr[] = "`$k`=NULL";
					}
				}

				$ret .= implode(', ', $arr);
				$i++;
				continue;
			}

			if ($nextchar == 'a')
         {
				$param = array_shift($array);
				$arr = array();

				foreach ($param as $v)
            {
					if (!is_null($v))
               {
						$v = mysql_real_escape_string($v, $this->link);
						$arr[] = "\"$v\"";
					}
               else
               {
						$arr[] = 'NULL';
					}
				}

				$ret .= implode(', ', $arr);
				$i++;
				continue;
			}

			if ($nextchar == 'n')
         {
				$param = array_shift($array);

				$ret .= intval($param);

				$i++;
				continue;
			}

			$param = array_shift($array);
			if (!is_null($param))
         {
				$param = mysql_real_escape_string($param, $this->link);
				$ret .= "\"$param\"";
			}
         else
         {
				$ret .= 'NULL';
			}

			if ($query[$i+1] == '?') $i++;
		}
      
		return $ret;
	}

	function _query_raw($query, $orig_query = NULL)
   {
		if (isset($_GET['debug'])) print $query . "<br>\n";

		$ret = NULL;

		if (config_get('SQL_LOGTIME'))
      {
			$time_start = microtime(true);

			$ret = mysql_query($query, $this->link);

			$time_end = microtime(true);
			$time = sprintf("%.3f", ($time_end - $time_start) * 1000);

			if (is_null($orig_query)) $orig_query = $query;
			$orig_query = preg_replace('/\s+/', ' ', $orig_query);
			$query = preg_replace('/\s+/', ' ', $query);

			$f = @fopen('data/sqltimes', 'a');
			@fwrite($f, base64_encode(serialize(array(time(), $time, $orig_query, $query))) . "\n");
			@fclose($f);
		}
      else
      {
			$ret = mysql_query($query, $this->link);
		}
		
		return $ret;
	}

	function query_array($args)
   {
		$query = array_shift($args);
		
		$orig_query = $query;
		$query = $this->substitute_params_array($query, $args);
		$rawres = $this->_query_raw($query, $orig_query);
		$res = new SQLResult($rawres);

		if ($error = mysql_error($this->link))
      {
			$res->error_message = "<b>SQL Query error:</b> $error<br>\n (query was: $query)<br>\n";
			if ((php_sapi_name() == 'cli' || config_get('DEV_SERVER')) && !isset($this->force_silent)) print $res->error_message;
		}

		$res->insert_id = mysql_insert_id($this->link);
		$res->affected = mysql_affected_rows($this->link);
		if ($rawres) $res->found = @mysql_num_rows($rawres);

		return $res;
	}

	function query()
   {
		$args = func_get_args();
		return $this->query_array($args);
	}

	static function wquery()
   {
		$dbw = SQL::get_writer_instance();
		$args = func_get_args();
		return $dbw->query_array($args);
	}

	function scalar_query()
   {
		$args = func_get_args();
		$res = $this->query_array($args);

		return $res->fetch_scalar();
	}

	static function static_scalar_query()
   {
		$dbr = SQL::get_reader_instance();
		$args = func_get_args();
		$res = $dbr->query_array($args);

		return $res->fetch_scalar();
	}

	/**
	 * @return массив объектов класса $classname
	 */
	static function collection_from_query()
   {
		$args = func_get_args();
		$classname = array_shift($args);
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
		$ret = array();
      
		while ($row = $res->fetch_hash())
      {
         $ret[] = new $classname($row['id'], $row);
      }
      
		return $ret;
	}

	static function collection_from_query_array($classname, $args)
   {
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
		$ret = array();
      
		while ($row = $res->fetch_hash())
      {
         $ret[] = new $classname($row['id'], $row);
      }
      
		return $ret;
	}

	/**
	 * @return массив ассоциативных массивов
	 */
	static function hash_collection_from_query()
   {
		$args = func_get_args();
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
		$ret = array();
      
		while ($row = $res->fetch_hash())
      {
         $ret[] = $row;
      }
		return $ret;
	}

	/**
	 * @return ассоциативный массив (‘название колонки’ => ‘значение’…)
	 */
	static function hash_from_query()
   {
		$args = func_get_args();
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
      
		if (!$res->found) return NULL;
      
		return $res->fetch_hash();
	}

	static function hash_collection_from_query_array($args)
   {
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
		$ret = array();
      
		while ($row = $res->fetch_hash())
      {
         $ret[] = $row;
      }
      
		return $ret;
	}

	static function stats_from_query_array($args)
   {
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
		$ret = array();
      
		while ($row = $res->fetch_hash())
      {
         $ret[] = array($row['obj'], $row['count']);
      }
      
		return $ret;
	}

	static function scalar_collection_from_query()
   {
		$args = func_get_args();
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
		$ret = array();
      
		while (($sc = $res->fetch_scalar()) !== NULL)
      {
         $ret[] = $sc;
      }
      
		return $ret;
	}

	static function scalar_collection_from_query_array($args)
   {
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
		$ret = array();
      
		while (($sc = $res->fetch_scalar()) !== NULL)
      {
         $ret[] = $sc;
      }
      
		return $ret;
	}

	static function object_from_query()
   {
		$args = func_get_args();
		$classname = array_shift($args);
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
      
		if (!$res->found) return NULL;
      
		$row = $res->fetch_hash();
      
		return new $classname($row['id'], $row);
	}

	/**
	 * @return объект класса $classname
	 */
	static function object_from_query_array($classname, $args)
   {
		$dbr = SQL::get_reader_instance();
		$res = $dbr->query_array($args);
      
		if (!$res->found) return NULL;
      
		$row = $res->fetch_hash();
      
		return new $classname($row['id'], $row);
	}
}

?>
